﻿using Microsoft.SharePoint;
using AgileShare.Framework.Core;
using AgileShare.Framework.Library.Helpers;
using AgileShare.Framework.SiteProvisioning.Definitions;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace AgileShare.Framework.SiteProvisioning.Helpers
{
	/// <summary>
	/// Helper to deploy List Definitions
	/// </summary>
	public static class ListDefinitionHelper
	{
		/// <summary>
		/// Ensures the list.
		/// </summary>
		/// <param name="listDef">The list instance.</param>
		/// <param name="web">The web.</param>
		/// <param name="updateIfExists">if set to <c>true</c> [update if exists].</param>
		/// <param name="updateViews">if set to <c>true</c> [update views].</param>
		/// <param name="replaceContentTypes">if set to <c>true</c> [replace content types].</param>
		/// <returns></returns>
		public static SPList EnsureList(ListInstanceDefinition listDef, SPWeb web, bool updateIfExists = false)
		{
			TraceProvider.Log(TraceProviderCategories.Default, (uint)TraceProviderEventIds.SPFieldHelper, TraceProviderSeverity.Information,
				"Ensuring List '{0}'; Relative URL: '{1}';", listDef.Title, listDef.RelativeUrl);

			SPList list = SPListHelper.TryGetListByRelativeUrl(listDef.RelativeUrl, web) ?? SPListHelper.TryGetListByTitle(listDef.Title, web);

			try
			{
				if (list == null)
				{
					SPListTemplate.QuickLaunchOptions quickLaunchOptions = SPListTemplate.QuickLaunchOptions.Default;
					if (listDef.OnQuickLaunch.HasValue)
					{
						quickLaunchOptions = (listDef.OnQuickLaunch == true) ? SPListTemplate.QuickLaunchOptions.On : SPListTemplate.QuickLaunchOptions.Off;
					}

					Guid? listId = null;

					bool hasCustomTemplate = !string.IsNullOrEmpty(listDef.TemplateName);
					if (hasCustomTemplate)
					{
						SPListTemplate template = SPListHelper.TryGetListTemplateByName(listDef.TemplateName, web.Site);
						if (template != null)
						{
							listId = web.Lists.Add(listDef.RelativeUrl, listDef.Description, template);
						}
					}
					else
					{
						listId = web.Lists.Add(listDef.RelativeUrl, listDef.Description, listDef.TemplateType);
					}

					if (listId.HasValue)
					{
						list = web.Lists[listId.Value];
						UpdateList(list, listDef, web, true);
					}
				}
				else if (updateIfExists)
				{
					UpdateList(list, listDef, web, false);
				}

				TraceProvider.Log(TraceProviderCategories.Default, (uint)TraceProviderEventIds.SiteProvisioning, TraceProviderSeverity.Monitorable,
					"Successfully ensured List '{0}'; Relative URL: '{1}';", listDef.Title, listDef.RelativeUrl);
			}
			catch (Exception ex)
			{
				TraceProvider.Log(TraceProviderCategories.Default, (uint)TraceProviderEventIds.SiteProvisioning, TraceProviderSeverity.CriticalError, ex,
					"Failed to ensure List '{0}'; Relative URL: '{1}';", listDef.Title, listDef.RelativeUrl);
			}

			return list;
		}

		/// <summary>
		/// Adds the lists.
		/// </summary>
		/// <param name="listDefs">The list defs.</param>
		/// <param name="web">The web.</param>
		/// <param name="updateIfExists">if set to <c>true</c> [update if exists].</param>
		/// <param name="replaceContentTypes">if set to <c>true</c> [replace content types].</param>
		/// <param name="updateViews">if set to <c>true</c> [update views].</param>
		public static void EnsureLists(IEnumerable<ListInstanceDefinition> listDefs, SPWeb web, bool updateIfExists = false)
		{
			foreach (ListInstanceDefinition listDef in listDefs)
			{
				EnsureList(listDef, web, updateIfExists);
			}
		}

		/// <summary>
		/// Updates the list.
		/// </summary>
		/// <param name="list">The list.</param>
		/// <param name="listDef">The list def.</param>
		/// <param name="web">The web.</param>
		/// <param name="updateViews">if set to <c>true</c> [update views].</param>
		/// <param name="replaceContentTypes">if set to <c>true</c> [replace content types].</param>
		public static void UpdateList(SPList list, ListInstanceDefinition listDef, SPWeb web, bool replaceContentTypes = false)
		{
			list.Title = listDef.Title;
			list.AllowDeletion = listDef.AllowDeletion ?? list.AllowDeletion;
			list.ContentTypesEnabled = listDef.ContentTypesEnabled ?? list.ContentTypesEnabled;
			list.EnableAttachments = listDef.EnableAttachments ?? list.EnableAttachments;
			list.EnableFolderCreation = listDef.EnableFolderCreation ?? list.EnableFolderCreation;
			list.EnableModeration = listDef.EnableModeration ?? list.EnableModeration;
			list.EnableSyndication = listDef.EnableSyndication ?? list.EnableSyndication;
			list.Hidden = listDef.Hidden ?? list.Hidden;
			list.OnQuickLaunch = listDef.OnQuickLaunch ?? list.OnQuickLaunch;

			list.Update();

			SPContentTypeHelper.AddSiteContentTypesToList(list, web.Site, replaceContentTypes, listDef.ContentTypeIds.ToArray());

			// TODO: Improve logic to set default View
			EnsureViews(listDef.ViewDefinitions.ToArray(), list, true);

			list.RootFolder.MoveTo(listDef.RelativeUrl);

			list.Update();
		}

		/// <summary>
		/// Updates the list.
		/// </summary>
		/// <param name="listDef">The list def.</param>
		/// <param name="web">The web.</param>
		/// <param name="replaceContentTypes">if set to <c>true</c> [replace content types].</param>
		/// <param name="updateViewsIfExist">if set to <c>true</c> [update views if exist].</param>
		/// <returns></returns>
		public static SPList UpdateList(ListInstanceDefinition listDef, SPWeb web, bool replaceContentTypes)
		{
			SPList list = SPListHelper.TryGetListByRelativeUrl(listDef.RelativeUrl, web);
			UpdateList(list, listDef, web, replaceContentTypes);

			return list;
		}

		/// <summary>
		/// Adds the list template.
		/// </summary>
		/// <param name="templateDef">The template def.</param>
		/// <param name="site">The site.</param>
		/// <param name="replaceIfExists">if set to <c>true</c> [replace if exists].</param>
		/// <returns></returns>
		public static SPListTemplate EnsureListTemplate(ListTemplateDefinition templateDef, SPSite site, bool replaceIfExists = false)
		{
			SPWeb web = site.RootWeb;

			if (replaceIfExists)
			{
				SPListHelper.DeleteListTemplate(templateDef.TemplateName, site);
			}

			var template = SPListHelper.TryGetListTemplateByName(templateDef.TemplateName, site);
			if (template == null)
			{
				string tempTitle = Guid.NewGuid().ToString("N");
				ListInstanceDefinition listDef = new ListInstanceDefinition
				{
					AllowDeletion = templateDef.AllowDeletion,
					ContentTypeIds = templateDef.ContentTypeIds,
					ContentTypesEnabled = templateDef.ContentTypesEnabled,
					Description = templateDef.Description,
					EnableAttachments = templateDef.EnableAttachments,
					EnableFolderCreation = templateDef.EnableFolderCreation,
					EnableModeration = templateDef.EnableModeration,
					EnableSyndication = templateDef.EnableSyndication,
					Hidden = templateDef.Hidden,
					OnQuickLaunch = templateDef.OnQuickLaunch,
					TemplateType = templateDef.TemplateType,
					ViewDefinitions = templateDef.ViewDefinitions,
					Title = tempTitle,
					RelativeUrl = tempTitle
				};

				SPList list = EnsureList(listDef, web, false);
				list.SaveAsTemplate(templateDef.FileName, templateDef.TemplateName, templateDef.Description, false);
				SPListHelper.DeleteListByTitle(tempTitle, web);

				template = SPListHelper.TryGetListTemplateByName(templateDef.TemplateName, site);
			}

			return template;
		}

		/// <summary>
		/// Adds the lists.
		/// </summary>
		/// <param name="templateDefs">The list defs.</param>
		/// <param name="site">The site.</param>
		/// <param name="replaceIfExists">if set to <c>true</c> [replace if exists].</param>
		public static void EnsureListTemplates(IEnumerable<ListTemplateDefinition> templateDefs, SPSite site, bool replaceIfExists = false)
		{
			foreach (ListTemplateDefinition templateDef in templateDefs)
			{
				EnsureListTemplate(templateDef, site, replaceIfExists);
			}
		}

		/// <summary>
		/// Adds the view.
		/// </summary>
		/// <param name="viewDefinition">The view definition.</param>
		/// <param name="list">The list.</param>
		/// <param name="updateIfExists">if set to <c>true</c> [update if exists].</param>
		/// <returns></returns>
		public static SPView EnsureView(ViewDefinition viewDefinition, SPList list, bool updateIfExists = false)
		{
			// TODO: Make parameters optional
			SPView view = SPListHelper.TryGetView(viewDefinition.Title, list);

			StringCollection viewFieldNames = new StringCollection();
			foreach (Guid fieldId in viewDefinition.ViewFieldIds)
			{
				if (list.Fields.Contains(fieldId))
				{
					viewFieldNames.Add(list.Fields[fieldId].InternalName);
				}
			}

			if (!SPListHelper.HasView(viewDefinition.Title, list))
			{
				view = list.Views.Add(viewDefinition.Title, viewFieldNames, viewDefinition.Query, viewDefinition.RowLimit, viewDefinition.Paged, viewDefinition.DefaultView,
					viewDefinition.ViewType, false);
			}
			else if (updateIfExists)
			{
				view.Title = viewDefinition.Title;
				view.Query = viewDefinition.Query;
				view.RowLimit = viewDefinition.RowLimit;
				view.Paged = viewDefinition.Paged;
				view.DefaultView = viewDefinition.DefaultView;
				
				foreach (string viewFieldName in viewFieldNames)
				{
					if (!view.ViewFields.Exists(viewFieldName))
					{
						view.ViewFields.Add(viewFieldName);
					}
				}
				view.Update();
			}

			return view;
		}

		/// <summary>
		/// Adds the views.
		/// </summary>
		/// <param name="viewDefinitions">The view definitions.</param>
		/// <param name="list">The list.</param>
		/// <param name="updateIfExists">if set to <c>true</c> [update if exists].</param>
		public static void EnsureViews(IEnumerable<ViewDefinition> viewDefinitions, SPList list, bool updateIfExists = false)
		{
			foreach (ViewDefinition viewDefinition in viewDefinitions)
			{
				EnsureView(viewDefinition, list, updateIfExists);
			}
		}


	}
}
